package cn.openea.perpartner.common.core.service.db;

import cn.openea.perpartner.common.core.exception.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.StopWatch;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import javax.annotation.Resource;
import java.util.List;
import java.util.function.BiFunction;

/**
 * @author ysk
 * @version 1.0.1
 * @Description mybatis数据批量操作优化
 */
@Slf4j
@Component
public class MyBatisBatchSaveAndFlush {

    /**
     * 一次批量操作最大可更改的数据条数
     * TODO 这里可以改为配置文件中指定
     */
    private static final int BATCH_SIZE = 500;

    @Resource
    private SqlSessionFactory sqlSessionFactory;

    /**
     * 批量插入和更新操作
     *
     * @param list        需要更改的数据
     * @param mapperClass Mapper
     * @param function    执行的方法
     * @param <E>         List
     * @param <T>         Mapper
     * @param <F>         执行的方法
     * @return
     */
    public <E, T, F> Integer batchUpdateOrInsert(List<E> list, Class<T> mapperClass, BiFunction<E, T, F> function) {
        if (list.isEmpty()) {
            throw new ServiceException("Error: list must not be empty");
        }
        // 初始化更改次数
        int i = 1;
        SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
        boolean actualTransactionActive = TransactionSynchronizationManager.isActualTransactionActive();
        StopWatch stopWatch = StopWatch.createStarted();
        try {
            // 如果没有事物的情况下，需要手动设置为false
            if (!actualTransactionActive) {
                sqlSession.getConnection().setAutoCommit(false);
            }
            T mapper = sqlSession.getMapper(mapperClass);
            int size = list.size();
            for (E item : list) {
                function.apply(item, mapper);
                if ((i % BATCH_SIZE == 0) || i == size) {
                    // 刷新缓存
                    sqlSession.flushStatements();
                }
                i++;
            }
            // 非事务环境下强制commit，事务情况下该commit相当于无效
            if (!actualTransactionActive) {
                sqlSession.getConnection().commit();
            }
            log.info("Success:batch insert executed successfully,success {} article data,time consuming {} second", i, stopWatch.getTime());
        } catch (Exception e) {
            log.error("Error:bulk insert failed The reason is {}", e.getMessage());
            sqlSession.rollback(!actualTransactionActive);
            // 抛出异常，如果有事物会回滚数据
            throw new ServiceException(e.getMessage());
        } finally {
            sqlSession.close();
        }
        return i - 1;
    }

}